import { Asset, Canvas, Component, ITweenOption, Node, Sprite, Texture2D, UITransform, Vec3, __private, assetManager, dragonBones, screen, resources, tween, warn, Tween, TweenAction, TweenSystem } from "cc";

export default class CC3Utils {
    /**
     * 加载远程资源Promise化
     * @param remoteUrl 远程资源URL地址
     * @returns 
     */
    public static loadRemoteRes(remoteUrl: string): Promise<any> {
        return new Promise<any>((resolve, reject) => {
            assetManager.loadRemote(remoteUrl, (error, data) => {
                if (error) {
                    warn(error);
                    reject(error);
                } else {
                    resolve(data);
                }
            })
        })
    }

    /**
     * 节点缩放
     * @param node 
     * @param duration 
     * @param scaleWeight 
     * @returns 
     */
    private static nodeScaleAniMap = new Map()
    public static nodeScaleAni(node: Node, duration: number = 0.1, scaleWeight: number = 0.1): Promise<void> {
        if (this.nodeScaleAniMap.has(node)) {
            return Promise.resolve()
        }
        this.nodeScaleAniMap.set(node, 1)
        return new Promise<void>((resolve, reject) => {
            let originalScale = node.scale.clone();
            // 播放
            tween(node)
                .to(duration, { scale: new Vec3(originalScale.x - scaleWeight, originalScale.y - scaleWeight, originalScale.z - scaleWeight) })
                .to(duration, { scale: originalScale })
                .call(() => {
                    this.nodeScaleAniMap.delete(node)
                    resolve()
                })
                .start();
        })
    }

    /**
     * 获取节点在目标节点（容器）下的相对位置
     * @param node 节点
     * @param container 目标节点（容器）
     */
    public static getRelativePosition(node: Node, container: Node): Vec3 {
        const worldPos = (node.getParent() || node).getComponent(UITransform).convertToWorldSpaceAR(node.getPosition());
        return container.parent.getComponent(UITransform).convertToNodeSpaceAR(worldPos);
    }

    /**
     * 两个节点是否重叠
     * @param node1 节点 1
     * @param node2 节点 2
     * @param contains 是否完全包含
     */
    public static areNodesOverlap(node1: Node, node2: Node, contains: boolean = false): boolean {
        const rect1 = node1.getComponent(UITransform).getBoundingBoxToWorld(),
            rect2 = node2.getComponent(UITransform).getBoundingBoxToWorld();
        return contains ? rect1.containsRect(rect2) : rect1.intersects(rect2);
    }

    /**
     * 抖动效果 
     * @param target 节点
     * @param rotateAngles 旋转大小
     * @param rotateSeconds 旋转时间
     * @param rotateRepeatNum 重复次数
     * @returns 
     */
    public static nodeShake(target: Node, rotateAngles: number = 8, rotateSeconds: number = 0.05, rotateRepeatNum: number = 3): Promise<void> {
        let org = target.angle
        return new Promise((resolve, reject) => {
            tween(target)
                .repeat(rotateRepeatNum, tween()
                    .to(rotateSeconds, { angle: org + rotateAngles })
                    .to(rotateSeconds * 2, { angle: org - rotateAngles })
                    .to(rotateSeconds, { angle: org })
                ).call(() => {
                    target.angle = org;
                    resolve();
                }).start();
        });
    }

    /**
     * 播放指定的龙骨动画
     * @param node 带有龙骨动画的节点
     * @param animation 要播放的动画名字 默认值是因为设计喜欢取这个动画名字
     * @param isSetNode 是否播放完后将节点隐藏
     * @param playNum 播放次数 -1为使用配置文件中的次数 0为无限循环播放
     * @param callbacks 每播放一次调用的回调函数
     * @returns 
     */
    public static playDragonBones(node: Node, animation: string = "newAnimation", isSetNode: boolean = true, playNum: number = 1, callbacks?: Function): Promise<void> {
        return new Promise<void>((resolve, reject) => {
            let num = 0;
            if (isSetNode) node.active = true;
            let dragonBonesComponent = node.getComponent(dragonBones.ArmatureDisplay);
            dragonBonesComponent.playAnimation(animation, playNum);
            dragonBonesComponent.on(dragonBones.EventObject.COMPLETE, function COMPLETE(event: any) {
                if (event.animationState.name == animation) {
                    callbacks && callbacks()
                    num++
                    if (num >= playNum) {
                        if (isSetNode) {
                            node.active = false;
                        }
                        dragonBonesComponent.off(dragonBones.EventObject.COMPLETE, COMPLETE, null)
                        resolve();
                    }
                }
            }, null)
        })
    }

    /**
     * tween.to方法的简单promise封装
     * @param node 节点
     * @param duration 持续时间同tween.to
     * @param props 参数同tween.to
     * @param opts 参数同tween.to
     * @returns 
     */
    public static tweenTo<T>(node: T, duration: number, props: __private._cocos_tween_tween__ConstructorType<T>, opts?: ITweenOption, setProps?: __private._cocos_tween_tween__ConstructorType<T>): Promise<void> {
        return new Promise<void>((resolve, reject) => {
            try {
                tween(node).set(setProps).to(duration, props, opts).call(() => {
                    resolve()
                }).start()
            } catch (error) {
                console.log(error);
                reject(error)
            }
        })
    }

    /**
     * 等待
     * @param time 时长（秒）
     * @returns dt两帧的间隔时间
     */
    public static sleep(time: number): Promise<void> {
        return new Promise(res => setTimeout(() => { res() }, time * 1000));
    }

    /**
     * 加载资源Promise化
     * @param path 资源相对resources的路径
     * @returns 
     */
    public static loadRes(path: string, AssetsType: any): Promise<any> {
        return new Promise<any>((resolve, reject) => {
            resources.load(path, AssetsType, (error, data) => {
                if (error) {
                    warn(error);
                    reject(error);
                } else {
                    resolve(data);
                }
            })
        })
    }

    /**
     * 返回设备尺寸
     * @returns 
     */
    public static getScreenSize(): { width: number, height: number } {
        let width = screen.windowSize.width / screen.devicePixelRatio
        let height = screen.windowSize.height / screen.devicePixelRatio
        return { width, height }
    }

    /**
   * 获取一个 min 到 max 范围内的随机整数
   * @param min 最小值
   * @param max 最大值
   * @param ranBurden 是否随机为正负数
   */
    public static getRandomInt(min: number = 0, max: number = 1, ranBurden: boolean = false): number {
        let flag = ranBurden ? Math.random() > 0.5 ? -1 : 1 : 1
        return Math.floor(Math.random() * (max - min) + min) * flag;
    }

    /**
     * 返回龙骨动画的时长(通过私有属性需要当前龙骨动画的节点active为true)
     * @param node 包含龙骨动画的节点
     * @param animation 需要统计的动画名称
     * @returns 
     */
    public static getDBTime(node: Node, animation: string = "newAnimation"): number {
        let orginActive = node.active;
        node.active = true
        let timer = node.getComponent(dragonBones.ArmatureDisplay).armature().animation.animations[animation].duration
        node.active = orginActive
        return timer
    }
}